home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / cvs-1_3.lha / cvs-1.3 / src / import.c < prev    next >
C/C++ Source or Header  |  1992-03-31  |  25KB  |  975 lines

  1. /*
  2.  * Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  * Copyright (c) 1989-1992, Brian Berliner
  4.  * 
  5.  * You may distribute under the terms of the GNU General Public License as
  6.  * specified in the README file that comes with the CVS 1.3 kit.
  7.  * 
  8.  * "import" checks in the vendor release located in the current directory into
  9.  * the CVS source repository.  The CVS vendor branch support is utilized.
  10.  * 
  11.  * At least three arguments are expected to follow the options:
  12.  *    repository    Where the source belongs relative to the CVSROOT
  13.  *    VendorTag    Vendor's major tag
  14.  *    VendorReleTag    Tag for this particular release
  15.  *
  16.  * Additional arguments specify more Vendor Release Tags.
  17.  */
  18.  
  19. #include "cvs.h"
  20.  
  21. #ifndef lint
  22. static char rcsid[] = "@(#)import.c 1.52 92/03/31";
  23. #endif
  24.  
  25. #define    FILE_HOLDER    ".#cvsxxx"
  26.  
  27. #if __STDC__
  28. static char *get_comment (char *user);
  29. static int add_rcs_file (char *message, char *rcs, char *user, char *vtag,
  30.                  int targc, char *targv[]);
  31. static int expand_at_signs (char *buf, off_t size, FILE *fp);
  32. static int add_rev (char *message, char *rcs, char *vfile, char *vers);
  33. static int add_tags (char *rcs, char *vfile, char *vtag, int targc,
  34.              char *targv[]);
  35. static int import_descend (char *message, char *vtag, int targc, char *targv[]);
  36. static int import_descend_dir (char *message, char *dir, char *vtag,
  37.                    int targc, char *targv[]);
  38. static int process_import_file (char *message, char *vfile, char *vtag,
  39.                 int targc, char *targv[]);
  40. static int update_rcs_file (char *message, char *vfile, char *vtag, int targc,
  41.                 char *targv[]);
  42. static void add_log (int ch, char *fname);
  43. #else
  44. static int import_descend ();
  45. static int process_import_file ();
  46. static int update_rcs_file ();
  47. static int add_rev ();
  48. static int add_tags ();
  49. static char *get_comment ();
  50. static int add_rcs_file ();
  51. static int expand_at_signs ();
  52. static void add_log ();
  53. static int import_descend_dir ();
  54. #endif                /* __STDC__ */
  55.  
  56. static int repos_len;
  57. static char vhead[50];
  58. static char vbranch[50];
  59. static FILE *logfp;
  60. static char repository[PATH_MAX];
  61. static int conflicts;
  62.  
  63. static char *import_usage[] =
  64. {
  65.     "Usage: %s %s [-Qq] [-I ign] [-m msg] [-b branch]\n",
  66.     "    repository vendor-tag release-tags...\n",
  67.     "\t-Q\tReally quiet.\n",
  68.     "\t-q\tSomewhat quiet.\n",
  69.     "\t-I ign\tMore files to ignore (! to reset).\n",
  70.     "\t-b bra\tVendor branch id.\n",
  71.     "\t-m msg\tLog message.\n",
  72.     NULL
  73. };
  74.  
  75. int
  76. import (argc, argv)
  77.     int argc;
  78.     char *argv[];
  79. {
  80.     char message[MAXMESGLEN];
  81.     char tmpfile[L_tmpnam+1];
  82.     char *cp;
  83.     int i, c, msglen, err;
  84.     List *ulist;
  85.     Node *p;
  86.  
  87.     if (argc == -1)
  88.     usage (import_usage);
  89.  
  90.     ign_setup ();
  91.  
  92.     (void) strcpy (vbranch, CVSBRANCH);
  93.     message[0] = '\0';
  94.     optind = 1;
  95.     while ((c = gnu_getopt (argc, argv, "Qqb:m:I:")) != -1)
  96.     {
  97.     switch (c)
  98.     {
  99.         case 'Q':
  100.         really_quiet = 1;
  101.         /* FALL THROUGH */
  102.         case 'q':
  103.         quiet = 1;
  104.         break;
  105.         case 'b':
  106.         (void) strcpy (vbranch, optarg);
  107.         break;
  108.         case 'm':
  109. #ifdef FORCE_USE_EDITOR
  110.         use_editor = TRUE;
  111. #else
  112.         use_editor = FALSE;
  113. #endif
  114.         if (strlen (optarg) >= (sizeof (message) - 1))
  115.         {
  116.             error (0, 0, "warning: message too long; truncated!");
  117.             (void) strncpy (message, optarg, sizeof (message));
  118.             message[sizeof (message) - 2] = '\0';
  119.         }
  120.         else
  121.             (void) strcpy (message, optarg);
  122.         break;
  123.         case 'I':
  124.         ign_add (optarg, 0);
  125.         break;
  126.         case '?':
  127.         default:
  128.         usage (import_usage);
  129.         break;
  130.     }
  131.     }
  132.     argc -= optind;
  133.     argv += optind;
  134.     if (argc < 3)
  135.     usage (import_usage);
  136.  
  137.     for (i = 1; i < argc; i++)        /* check the tags for validity */
  138.     RCS_check_tag (argv[i]);
  139.  
  140.     /* XXX - this should be a module, not just a pathname */
  141.     if (argv[0][0] != '/')
  142.     {
  143.     if (CVSroot == NULL)
  144.     {
  145.         error (0, 0, "missing CVSROOT environment variable\n");
  146.         error (1, 0, "Set it or specify the '-d' option to %s.",
  147.            program_name);
  148.     }
  149.     (void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
  150.     repos_len = strlen (CVSroot);
  151.     }
  152.     else
  153.     {
  154.     (void) strcpy (repository, argv[0]);
  155.     repos_len = 0;
  156.     }
  157.  
  158.     /*
  159.      * Consistency checks on the specified vendor branch.  It must be
  160.      * composed of only numbers and dots ('.').  Also, for now we only
  161.      * support branching to a single level, so the specified vendor branch
  162.      * must only have two dots in it (like "1.1.1").
  163.      */
  164.     for (cp = vbranch; *cp != '\0'; cp++)
  165.     if (!isdigit (*cp) && *cp != '.')
  166.         error (1, 0, "%s is not a numeric branch", vbranch);
  167.     if (numdots (vbranch) != 2)
  168.     error (1, 0, "Only branches with two dots are supported: %s", vbranch);
  169.     (void) strcpy (vhead, vbranch);
  170.     cp = rindex (vhead, '.');
  171.     *cp = '\0';
  172.     if (use_editor)
  173.     do_editor ((char *) NULL, message, repository, (List *) NULL);
  174.     msglen = strlen (message);
  175.     if (msglen == 0 || message[msglen - 1] != '\n')
  176.     {
  177.     message[msglen] = '\n';
  178.     message[msglen + 1] = '\0';
  179.     }
  180.  
  181.     /*
  182.      * Make all newly created directories writable.  Should really use a more
  183.      * sophisticated security mechanism here.
  184.      */
  185.     (void) umask (2);
  186.     make_directories (repository);
  187.  
  188.     /* Create the logfile that will be logged upon completion */
  189.     if ((logfp = fopen (tmpnam (tmpfile), "w+")) == NULL)
  190.     error (1, errno, "cannot create temporary file `%s'", tmpfile);
  191.     (void) unlink (tmpfile);        /* to be sure it goes away */
  192.     (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]);
  193.     (void) fprintf (logfp, "Release Tags:\t");
  194.     for (i = 2; i < argc; i++)
  195.     (void) fprintf (logfp, "%s\n\t\t", argv[i]);
  196.     (void) fprintf (logfp, "\n");
  197.  
  198.     /* Just Do It.  */
  199.     err = import_descend (message, argv[1], argc - 2, argv + 2);
  200.     if (conflicts)
  201.     {
  202.     if (!really_quiet)
  203.     {
  204.         (void) printf ("\n%d conflicts created by this import.\n",
  205.                conflicts);
  206.         (void) printf ("Use the following command to help the merge:\n\n");
  207.         (void) printf ("\t%s checkout -j%s:yesterday -j%s %s\n\n",
  208.                program_name, argv[1], argv[1], argv[0]);
  209.     }
  210.  
  211.     (void) fprintf (logfp, "\n%d conflicts created by this import.\n",
  212.             conflicts);
  213.     (void) fprintf (logfp,
  214.             "Use the following command to help the merge:\n\n");
  215.     (void) fprintf (logfp, "\t%s checkout -j%s:yesterday -j%s %s\n\n",
  216.             program_name, argv[1], argv[1], argv[0]);
  217.     }
  218.     else
  219.     {
  220.     if (!really_quiet)
  221.         (void) printf ("\nNo conflicts created by this import\n\n");
  222.     (void) fprintf (logfp, "\nNo conflicts created by this import\n\n");
  223.     }
  224.  
  225.     /*
  226.      * Write out the logfile and clean up.
  227.      */
  228.     ulist = getlist ();
  229.     p = getnode ();
  230.     p->type = UPDATE;
  231.     p->delproc = update_delproc;
  232.     p->key = xstrdup ("- Imported sources");
  233.     p->data = (char *) T_TITLE;
  234.     (void) addnode (ulist, p);
  235.     Update_Logfile (repository, message, vbranch, logfp, ulist);
  236.     dellist (&ulist);
  237.     (void) fclose (logfp);
  238.     return (err);
  239. }
  240.  
  241. /*
  242.  * process all the files in ".", then descend into other directories.
  243.  */
  244. static int
  245. import_descend (message, vtag, targc, targv)
  246.     char *message;
  247.     char *vtag;
  248.     int targc;
  249.     char *targv[];
  250. {
  251.     DIR *dirp;
  252.     struct direct *dp;
  253.     int err = 0;
  254.     int has_dirs = 0;
  255.  
  256.     /* first, load up any per-directory ignore lists */
  257.     ign_add_file (CVSDOTIGNORE, 1);
  258.  
  259.     if ((dirp = opendir (".")) == NULL)
  260.     {
  261.     err++;
  262.     }
  263.     else
  264.     {
  265.     while ((dp = readdir (dirp)) != NULL)
  266.     {
  267.         if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
  268.         continue;
  269.         if (ign_name (dp->d_name))
  270.         {
  271.         add_log ('I', dp->d_name);
  272.         continue;
  273.         }
  274.         if (isdir (dp->d_name))
  275.         {
  276.         has_dirs = 1;
  277.         }
  278.         else
  279.         {
  280.         if (islink (dp->d_name))
  281.         {
  282.             add_log ('L', dp->d_name);
  283.             err++;
  284.         }
  285.         else
  286.         {
  287.             err += process_import_file (message, dp->d_name,
  288.                         vtag, targc, targv);
  289.         }
  290.         }
  291.     }
  292.     (void) closedir (dirp);
  293.     }
  294.     if (has_dirs)
  295.     {
  296.     if ((dirp = opendir (".")) == NULL)
  297.         err++;
  298.     else
  299.     {
  300.         while ((dp = readdir (dirp)) != NULL)
  301.         {
  302.         if (ign_name (dp->d_name) || !isdir (dp->d_name))
  303.             continue;
  304.         err += import_descend_dir (message, dp->d_name,
  305.                        vtag, targc, targv);
  306.         }
  307.         (void) closedir (dirp);
  308.     }
  309.     }
  310.     return (err);
  311. }
  312.  
  313. /*
  314.  * Process the argument import file.
  315.  */
  316. static int
  317. process_import_file (message, vfile, vtag, targc, targv)
  318.     char *message;
  319.     char *vfile;
  320.     char *vtag;
  321.     int targc;
  322.     char *targv[];
  323. {
  324.     char attic_name[PATH_MAX];
  325.     char rcs[PATH_MAX];
  326.  
  327.     (void) sprintf (rcs, "%s/%s%s", repository, vfile, RCSEXT);
  328.     if (!isfile (rcs))
  329.     {
  330.     (void) sprintf (attic_name, "%s/%s/%s%s", repository, CVSATTIC,
  331.             vfile, RCSEXT);
  332.     if (!isfile (attic_name))
  333.     {
  334.  
  335.         /*
  336.          * A new import source file; it doesn't exist as a ,v within the
  337.          * repository nor in the Attic -- create it anew.
  338.          */
  339.         add_log ('N', vfile);
  340.         return (add_rcs_file (message, rcs, vfile, vtag, targc, targv));
  341.     }
  342.     }
  343.  
  344.     /*
  345.      * an rcs file exists. have to do things the official, slow, way.
  346.      */
  347.     return (update_rcs_file (message, vfile, vtag, targc, targv));
  348. }
  349.  
  350. /*
  351.  * The RCS file exists; update it by adding the new import file to the
  352.  * (possibly already existing) vendor branch.
  353.  */
  354. static int
  355. update_rcs_file (message, vfile, vtag, targc, targv)
  356.     char *message;
  357.     char *vfile;
  358.     char *vtag;
  359.     int targc;
  360.     char *targv[];
  361. {
  362.     Vers_TS *vers;
  363.     char letter;
  364.     int ierrno;
  365.  
  366.     vers = Version_TS (repository, (char *) NULL, vbranch, (char *) NULL, vfile,
  367.                1, 0, (List *) NULL, (List *) NULL);
  368.     if (vers->vn_rcs != NULL)
  369.     {
  370.     char xtmpfile[50];
  371.     int different;
  372.     int retcode = 0;
  373.  
  374.     /* XXX - should be more unique */
  375.     (void) sprintf (xtmpfile, "/tmp/%s", FILE_HOLDER);
  376.  
  377.     /*
  378.      * The rcs file does have a revision on the vendor branch. Compare
  379.      * this revision with the import file; if they match exactly, there
  380.      * is no need to install the new import file as a new revision to the
  381.      * branch.  Just tag the revision with the new import tags.
  382.      * 
  383.      * This is to try to cut down the number of "C" conflict messages for
  384.      * locally modified import source files.
  385.      */
  386. #ifdef HAVE_RCS5
  387.     run_setup ("%s%s -q -f -r%s -p -ko", Rcsbin, RCS_CO, vers->vn_rcs);
  388. #else
  389.     run_setup ("%s%s -q -f -r%s -p", Rcsbin, RCS_CO, vers->vn_rcs);
  390. #endif
  391.     run_arg (vers->srcfile->path);
  392.     if ((retcode = run_exec (RUN_TTY, xtmpfile, RUN_TTY,
  393.                  RUN_NORMAL|RUN_REALLY)) != 0)
  394.     {
  395.         ierrno = errno;
  396.         fperror (logfp, 0, retcode == -1 ? ierrno : 0,
  397.              "ERROR: cannot co revision %s of file %s", vers->vn_rcs,
  398.              vers->srcfile->path);
  399.         error (0, retcode == -1 ? ierrno : 0,
  400.            "ERROR: cannot co revision %s of file %s", vers->vn_rcs,
  401.            vers->srcfile->path);
  402.         (void) unlink_file (xtmpfile);
  403.         return (1);
  404.     }
  405.     different = xcmp (xtmpfile, vfile);
  406.     (void) unlink_file (xtmpfile);
  407.     if (!different)
  408.     {
  409.         int retval = 0;
  410.  
  411.         /*
  412.          * The two files are identical.  Just update the tags, print the
  413.          * "U", signifying that the file has changed, but needs no
  414.          * attention, and we're done.
  415.          */
  416.         if (add_tags (vers->srcfile->path, vfile, vtag, targc, targv))
  417.         retval = 1;
  418.         add_log ('U', vfile);
  419.         freevers_ts (&vers);
  420.         return (retval);
  421.     }
  422.     }
  423.  
  424.     /* We may have failed to parse the RCS file; check just in case */
  425.     if (vers->srcfile == NULL || add_rev (message, vers->srcfile->path,
  426.                       vfile, vers->vn_rcs) ||
  427.     add_tags (vers->srcfile->path, vfile, vtag, targc, targv))
  428.     {
  429.     freevers_ts (&vers);
  430.     return (1);
  431.     }
  432.  
  433.     if (vers->srcfile->branch == NULL ||
  434.     strcmp (vers->srcfile->branch, vbranch) != 0)
  435.     {
  436.     conflicts++;
  437.     letter = 'C';
  438.     }
  439.     else
  440.     letter = 'U';
  441.     add_log (letter, vfile);
  442.  
  443.     freevers_ts (&vers);
  444.     return (0);
  445. }
  446.  
  447. /*
  448.  * Add the revision to the vendor branch
  449.  */
  450. static int
  451. add_rev (message, rcs, vfile, vers)
  452.     char *message;
  453.     char *rcs;
  454.     char *vfile;
  455.     char *vers;
  456. {
  457.     int locked, status, ierrno;
  458.     int retcode = 0;
  459.  
  460.     if (noexec)
  461.     return (0);
  462.  
  463.     locked = 0;
  464.     if (vers != NULL)
  465.     {
  466.     run_setup ("%s%s -q -l%s", Rcsbin, RCS, vbranch);
  467.     run_arg (rcs);
  468.     if ((retcode = run_exec (RUN_TTY, DEVNULL, DEVNULL, RUN_NORMAL)) == 0)
  469.         locked = 1;
  470.     else if (retcode == -1)
  471.     {
  472.         error (0, errno, "fork failed");
  473.         return (1);
  474.     }
  475.     }
  476.     if (link_file (vfile, FILE_HOLDER) < 0)
  477.     {
  478.     if (errno == EEXIST)
  479.     {
  480.         (void) unlink_file (FILE_HOLDER);
  481.         (void) link_file (vfile, FILE_HOLDER);
  482.     }
  483.     else
  484.     {
  485.         ierrno = errno;
  486.         fperror (logfp, 0, ierrno, "ERROR: cannot create link to %s", vfile);
  487.         error (0, ierrno, "ERROR: cannot create link to %s", vfile);
  488.         return (1);
  489.     }
  490.     }
  491.     run_setup ("%s%s -q -f -r%s", Rcsbin, RCS_CI, vbranch);
  492.     run_args ("-m%s", message);
  493.     run_arg (rcs);
  494.     status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
  495.     ierrno = errno;
  496.     rename_file (FILE_HOLDER, vfile);
  497.     if (status)
  498.     {
  499.     if (!noexec)
  500.     {
  501.         fperror (logfp, 0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs);
  502.         error (0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs);
  503.     }
  504.     if (locked)
  505.     {
  506.         run_setup ("%s%s -q -u%s", Rcsbin, RCS, vbranch);
  507.         run_arg (rcs);
  508.         (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
  509.     }
  510.     return (1);
  511.     }
  512.     return (0);
  513. }
  514.  
  515. /*
  516.  * Add the vendor branch tag and all the specified import release tags to the
  517.  * RCS file.  The vendor branch tag goes on the branch root (1.1.1) while the
  518.  * vendor release tags go on the newly added leaf of the branch (1.1.1.1,
  519.  * 1.1.1.2, ...).
  520.  */
  521. static int
  522. add_tags (rcs, vfile, vtag, targc, targv)
  523.     char *rcs;
  524.     char *vfile;
  525.     char *vtag;
  526.     int targc;
  527.     char *targv[];
  528. {
  529.     int i, ierrno;
  530.     Vers_TS *vers;
  531.     int retcode = 0;
  532.  
  533.     if (noexec)
  534.     return (0);
  535.  
  536.     run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, vtag, vbranch);
  537.     run_arg (rcs);
  538.     if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
  539.     {
  540.     ierrno = errno;
  541.     fperror (logfp, 0, retcode == -1 ? ierrno : 0, 
  542.          "ERROR: Failed to set tag %s in %s", vtag, rcs);
  543.     error (0, retcode == -1 ? ierrno : 0,
  544.            "ERROR: Failed to set tag %s in %s", vtag, rcs);
  545.     return (1);
  546.     }
  547.     vers = Version_TS (repository, (char *) NULL, vtag, (char *) NULL, vfile,
  548.                1, 0, (List *) NULL, (List *) NULL);
  549.     for (i = 0; i < targc; i++)
  550.     {
  551.     run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, targv[i], vers->vn_rcs);
  552.     run_arg (rcs);
  553.     if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
  554.     {
  555.         ierrno = errno;
  556.         fperror (logfp, 0, retcode == -1 ? ierrno : 0, 
  557.              "WARNING: Couldn't add tag %s to %s", targv[i], rcs);
  558.         error (0, retcode == -1 ? ierrno : 0,
  559.            "WARNING: Couldn't add tag %s to %s", targv[i], rcs);
  560.     }
  561.     }
  562.     freevers_ts (&vers);
  563.     return (0);
  564. }
  565.  
  566. /*
  567.  * Stolen from rcs/src/rcsfnms.c, and adapted/extended.
  568.  */
  569. struct compair
  570. {
  571.     char *suffix, *comlead;
  572. };
  573.  
  574. struct compair comtable[] =
  575. {
  576.  
  577. /*
  578.  * comtable pairs each filename suffix with a comment leader. The comment
  579.  * leader is placed before each line generated by the $Log keyword. This
  580.  * table is used to guess the proper comment leader from the working file's
  581.  * suffix during initial ci (see InitAdmin()). Comment leaders are needed for
  582.  * languages without multiline comments; for others they are optional.
  583.  */
  584.     "a", "-- ",                /* Ada         */
  585.     "ada", "-- ",
  586.     "asm", ";; ",            /* assembler (MS-DOS) */
  587.     "bat", ":: ",            /* batch (MS-DOS) */
  588.     "c", " * ",                /* C         */
  589.     "c++", "// ",            /* C++ in all its infinite guises */
  590.     "cc", "// ",
  591.     "cpp", "// ",
  592.     "cxx", "// ",
  593.     "cl", ";;; ",            /* Common Lisp     */
  594.     "cmd", ":: ",            /* command (OS/2) */
  595.     "cmf", "c ",            /* CM Fortran     */
  596.     "cs", " * ",            /* C*         */
  597.     "csh", "# ",            /* shell     */
  598.     "e", "# ",                /* efl         */
  599.     "el", "; ",                /* Emacs Lisp     */
  600.     "f", "c ",                /* Fortran     */
  601.     "for", "c ",
  602.     "h", " * ",                /* C-header     */
  603.     "hh", "// ",            /* C++ header     */
  604.     "hpp", "// ",
  605.     "hxx", "// ",
  606.     "in", "# ",                /* for Makefile.in */
  607.     "l", " * ",                /* lex (conflict between lex and
  608.                      * franzlisp) */
  609.     "mac", ";; ",            /* macro (DEC-10, MS-DOS, PDP-11,
  610.                      * VMS, etc) */
  611.     "me", ".\\\" ",            /* me-macros    t/nroff     */
  612.     "ml", "; ",                /* mocklisp     */
  613.     "mm", ".\\\" ",            /* mm-macros    t/nroff     */
  614.     "ms", ".\\\" ",            /* ms-macros    t/nroff     */
  615.     "man", ".\\\" ",            /* man-macros    t/nroff     */
  616.     "1", ".\\\" ",            /* feeble attempt at man pages... */
  617.     "2", ".\\\" ",
  618.     "3", ".\\\" ",
  619.     "4", ".\\\" ",
  620.     "5", ".\\\" ",
  621.     "6", ".\\\" ",
  622.     "7", ".\\\" ",
  623.     "8", ".\\\" ",
  624.     "9", ".\\\" ",
  625.     "p", " * ",                /* pascal     */
  626.     "pas", " * ",
  627.     "pl", "# ",                /* perl    (conflict with Prolog) */
  628.     "ps", "% ",                /* postscript     */
  629.     "r", "# ",                /* ratfor     */
  630.     "red", "% ",            /* psl/rlisp     */
  631. #ifdef sparc
  632.     "s", "! ",                /* assembler     */
  633. #endif
  634. #ifdef mc68000
  635.     "s", "| ",                /* assembler     */
  636. #endif
  637. #ifdef pdp11
  638.     "s", "/ ",                /* assembler     */
  639. #endif
  640. #ifdef vax
  641.     "s", "# ",                /* assembler     */
  642. #endif
  643. #ifdef __ksr__
  644.     "s", "# ",                /* assembler     */
  645.     "S", "# ",                /* Macro assembler */
  646. #endif
  647.     "sh", "# ",                /* shell     */
  648.     "sl", "% ",                /* psl         */
  649.     "tex", "% ",            /* tex         */
  650.     "y", " * ",                /* yacc         */
  651.     "ye", " * ",            /* yacc-efl     */
  652.     "yr", " * ",            /* yacc-ratfor     */
  653.     "", "# ",                /* default for empty suffix     */
  654.     NULL, "# "                /* default for unknown suffix;     */
  655. /* must always be last         */
  656. };
  657.  
  658. static char *
  659. get_comment (user)
  660.     char *user;
  661. {
  662.     char *cp, *suffix;
  663.     char suffix_path[PATH_MAX];
  664.     int i;
  665.  
  666.     cp = rindex (user, '.');
  667.     if (cp != NULL)
  668.     {
  669.     cp++;
  670.  
  671.     /*
  672.      * Convert to lower-case, since we are not concerned about the
  673.      * case-ness of the suffix.
  674.      */
  675.     (void) strcpy (suffix_path, cp);
  676.     for (cp = suffix_path; *cp; cp++)
  677.         if (isupper (*cp))
  678.         *cp = tolower (*cp);
  679.     suffix = suffix_path;
  680.     }
  681.     else
  682.     suffix = "";            /* will use the default */
  683.     for (i = 0;; i++)
  684.     {
  685.     if (comtable[i].suffix == NULL)    /* default */
  686.         return (comtable[i].comlead);
  687.     if (strcmp (suffix, comtable[i].suffix) == 0)
  688.         return (comtable[i].comlead);
  689.     }
  690. }
  691.  
  692. static int
  693. add_rcs_file (message, rcs, user, vtag, targc, targv)
  694.     char *message;
  695.     char *rcs;
  696.     char *user;
  697.     char *vtag;
  698.     int targc;
  699.     char *targv[];
  700. {
  701.     FILE *fprcs, *fpuser;
  702.     struct stat sb;
  703.     struct tm *ftm;
  704.     time_t now;
  705.     char altdate1[50], altdate2[50];
  706.     char *author, *buf;
  707.     int i, mode, ierrno, err = 0;
  708.  
  709.     if (noexec)
  710.     return (0);
  711.  
  712.     fprcs = open_file (rcs, "w+");
  713.     fpuser = open_file (user, "r");
  714.  
  715.     /*
  716.      * putadmin()
  717.      */
  718.     if (fprintf (fprcs, "head     %s;\n", vhead) == EOF ||
  719.     fprintf (fprcs, "branch   %s;\n", vbranch) == EOF ||
  720.     fprintf (fprcs, "access   ;\n") == EOF ||
  721.     fprintf (fprcs, "symbols  ") == EOF)
  722.     {
  723.     goto write_error;
  724.     }
  725.  
  726.     for (i = targc - 1; i >= 0; i--)    /* RCS writes the symbols backwards */
  727.     if (fprintf (fprcs, "%s:%s.1 ", targv[i], vbranch) == EOF)
  728.         goto write_error;
  729.  
  730.     if (fprintf (fprcs, "%s:%s;\n", vtag, vbranch) == EOF ||
  731.     fprintf (fprcs, "locks    ; strict;\n") == EOF ||
  732.     /* XXX - make sure @@ processing works in the RCS file */
  733.     fprintf (fprcs, "comment  @%s@;\n\n", get_comment (user)) == EOF)
  734.     {
  735.     goto write_error;
  736.     }
  737.  
  738.     /*
  739.      * puttree()
  740.      */
  741.     (void) time (&now);
  742. #ifdef HAVE_RCS5
  743.     ftm = gmtime (&now);
  744. #else
  745.     ftm = localtime (&now);
  746. #endif
  747.     (void) sprintf (altdate1, DATEFORM,
  748.             ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
  749.             ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
  750.             ftm->tm_min, ftm->tm_sec);
  751.     now++;
  752. #ifdef HAVE_RCS5
  753.     ftm = gmtime (&now);
  754. #else
  755.     ftm = localtime (&now);
  756. #endif
  757.     (void) sprintf (altdate2, DATEFORM,
  758.             ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
  759.             ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
  760.             ftm->tm_min, ftm->tm_sec);
  761.     author = getcaller ();
  762.  
  763.     if (fprintf (fprcs, "\n%s\n", vhead) == EOF ||
  764.     fprintf (fprcs, "date     %s;  author %s;  state Exp;\n",
  765.          altdate1, author) == EOF ||
  766.     fprintf (fprcs, "branches %s.1;\n", vbranch) == EOF ||
  767.     fprintf (fprcs, "next     ;\n") == EOF ||
  768.     fprintf (fprcs, "\n%s.1\n", vbranch) == EOF ||
  769.     fprintf (fprcs, "date     %s;  author %s;  state Exp;\n",
  770.          altdate2, author) == EOF ||
  771.     fprintf (fprcs, "branches ;\n") == EOF ||
  772.     fprintf (fprcs, "next     ;\n\n") == EOF ||
  773.     /*
  774.      * putdesc()
  775.      */
  776.     fprintf (fprcs, "\ndesc\n") == EOF ||
  777.     fprintf (fprcs, "@@\n\n\n") == EOF ||
  778.     /*
  779.      * putdelta()
  780.      */
  781.     fprintf (fprcs, "\n%s\n", vhead) == EOF ||
  782.     fprintf (fprcs, "log\n") == EOF ||
  783.     fprintf (fprcs, "@Initial revision\n@\n") == EOF ||
  784.     fprintf (fprcs, "text\n@") == EOF)
  785.     {
  786.     goto write_error;
  787.     }
  788.  
  789.     if (fstat (fileno (fpuser), &sb) < 0)
  790.     error (1, errno, "cannot fstat %s", user);
  791.     if (sb.st_size > 0)
  792.     {
  793.     off_t size;
  794.  
  795.     size = sb.st_size;
  796.     buf = xmalloc ((int) size);
  797.     if (fread (buf, (int) size, 1, fpuser) != 1)
  798.         error (1, errno, "cannot read file %s for copying", user);
  799.     if (expand_at_signs (buf, size, fprcs) == EOF)
  800.         goto write_error;
  801.     free (buf);
  802.     }
  803.     if (fprintf (fprcs, "@\n\n") == EOF ||
  804.     fprintf (fprcs, "\n%s.1\n", vbranch) == EOF ||
  805.     fprintf (fprcs, "log\n@") == EOF ||
  806.     expand_at_signs (message, (off_t) strlen (message), fprcs) == EOF ||
  807.     fprintf (fprcs, "@\ntext\n") == EOF ||
  808.     fprintf (fprcs, "@@\n") == EOF)
  809.     {
  810.     goto write_error;
  811.     }
  812.     if (fclose (fprcs) == EOF)
  813.     {
  814.     ierrno = errno;
  815.     goto write_error_noclose;
  816.     }
  817.     (void) fclose (fpuser);
  818.  
  819.     /*
  820.      * Fix the modes on the RCS files.  They must maintain the same modes as
  821.      * the original user file, except that all write permissions must be
  822.      * turned off.
  823.      */
  824.     mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH);
  825.     if (chmod (rcs, mode) < 0)
  826.     {
  827.     ierrno = errno;
  828.     fperror (logfp, 0, ierrno,
  829.          "WARNING: cannot change mode of file %s", rcs);
  830.     error (0, ierrno, "WARNING: cannot change mode of file %s", rcs);
  831.     err++;
  832.     }
  833.     return (err);
  834.  
  835. write_error:
  836.     ierrno = errno;
  837.     (void) fclose (fprcs);
  838. write_error_noclose:
  839.     (void) fclose (fpuser);
  840.     fperror (logfp, 0, ierrno, "ERROR: cannot write file %s", rcs);
  841.     error (0, ierrno, "ERROR: cannot write file %s", rcs);
  842.     if (ierrno == ENOSPC)
  843.     {
  844.     (void) unlink (rcs);
  845.     fperror (logfp, 0, 0, "ERROR: out of space - aborting");
  846.     error (1, 0, "ERROR: out of space - aborting");
  847.     }
  848.     return (err + 1);
  849. }
  850.  
  851. /*
  852.  * Sigh..  need to expand @ signs into double @ signs
  853.  */
  854. static int
  855. expand_at_signs (buf, size, fp)
  856.     char *buf;
  857.     off_t size;
  858.     FILE *fp;
  859. {
  860.     char *cp, *end;
  861.  
  862.     for (cp = buf, end = buf + size; cp < end; cp++)
  863.     {
  864.     if (*cp == '@')
  865.         (void) putc ('@', fp);
  866.     if (putc (*cp, fp) == EOF)
  867.         return (EOF);
  868.     }
  869.     return (1);
  870. }
  871.  
  872. /*
  873.  * Write an update message to (potentially) the screen and the log file.
  874.  */
  875. static void
  876. add_log (ch, fname)
  877.     char ch;
  878.     char *fname;
  879. {
  880.     if (!really_quiet)            /* write to terminal */
  881.     {
  882.     if (repos_len)
  883.         (void) printf ("%c %s/%s\n", ch, repository + repos_len + 1, fname);
  884.     else if (repository[0])
  885.         (void) printf ("%c %s/%s\n", ch, repository, fname);
  886.     else
  887.         (void) printf ("%c %s\n", ch, fname);
  888.     }
  889.  
  890.     if (repos_len)            /* write to logfile */
  891.     (void) fprintf (logfp, "%c %s/%s\n", ch,
  892.             repository + repos_len + 1, fname);
  893.     else if (repository[0])
  894.     (void) fprintf (logfp, "%c %s/%s\n", ch, repository, fname);
  895.     else
  896.     (void) fprintf (logfp, "%c %s\n", ch, fname);
  897. }
  898.  
  899. /*
  900.  * This is the recursive function that walks the argument directory looking
  901.  * for sub-directories that have CVS administration files in them and updates
  902.  * them recursively.
  903.  * 
  904.  * Note that we do not follow symbolic links here, which is a feature!
  905.  */
  906. static int
  907. import_descend_dir (message, dir, vtag, targc, targv)
  908.     char *message;
  909.     char *dir;
  910.     char *vtag;
  911.     int targc;
  912.     char *targv[];
  913. {
  914.     char cwd[PATH_MAX];
  915.     char *cp;
  916.     int ierrno, err;
  917.  
  918.     if (islink (dir))
  919.     return (0);
  920.     if (getwd (cwd) == NULL)
  921.     {
  922.     fperror (logfp, 0, 0, "ERROR: cannot get working directory: %s", cwd);
  923.     error (0, 0, "ERROR: cannot get working directory: %s", cwd);
  924.     return (1);
  925.     }
  926.     if (repository[0] == '\0')
  927.     (void) strcpy (repository, dir);
  928.     else
  929.     {
  930.     (void) strcat (repository, "/");
  931.     (void) strcat (repository, dir);
  932.     }
  933.     if (!quiet)
  934.     error (0, 0, "Importing %s", repository);
  935.     if (chdir (dir) < 0)
  936.     {
  937.     ierrno = errno;
  938.     fperror (logfp, 0, ierrno, "ERROR: cannot chdir to %s", repository);
  939.     error (0, ierrno, "ERROR: cannot chdir to %s", repository);
  940.     err = 1;
  941.     goto out;
  942.     }
  943.     if (!isdir (repository))
  944.     {
  945.     if (isfile (repository))
  946.     {
  947.         fperror (logfp, 0, 0, "ERROR: %s is a file, should be a directory!",
  948.              repository);
  949.         error (0, 0, "ERROR: %s is a file, should be a directory!",
  950.            repository);
  951.         err = 1;
  952.         goto out;
  953.     }
  954.     if (noexec == 0 && mkdir (repository, 0777) < 0)
  955.     {
  956.         ierrno = errno;
  957.         fperror (logfp, 0, ierrno,
  958.              "ERROR: cannot mkdir %s -- not added", repository);
  959.         error (0, ierrno,
  960.            "ERROR: cannot mkdir %s -- not added", repository);
  961.         err = 1;
  962.         goto out;
  963.     }
  964.     }
  965.     err = import_descend (message, vtag, targc, targv);
  966.   out:
  967.     if ((cp = rindex (repository, '/')) != NULL)
  968.     *cp = '\0';
  969.     else
  970.     repository[0] = '\0';
  971.     if (chdir (cwd) < 0)
  972.     error (1, errno, "cannot chdir to %s", cwd);
  973.     return (err);
  974. }
  975.